package changceilingcolor003;

//import toxi.color.ColorTheme;
import java.util.ArrayList;
import java.util.List;

import toxi.geom.Vec2D;
import toxi.geom.Vec3D;


import processing.core.PApplet;


public class Person extends Actors {


	//Vec3D pos;
	//Actors destiny;
	int destiny;
	int m_type;
	//
	Vec3D vel;//Velocity?
	float setVel;//Curen
	float maxVel;//Maximal Velocity?
	Vec3D acc;//Acceleration
	int myColor;

	Vec3D CurrentTarget;//Velocity?

	float maxForce;
	float intention;

	boolean there=false;
	float wandertheta;
	float attention_radios;
	//Vec3D distraction;///////////////////////////////////////////////////////////////
	int attention;//?
	int time_to_go_to_attraction;
	boolean distracted=false;
	int time_pass_since_last_attraction;
	int timer;
	float pHt=random1(18, 22);//person's height
	
	ArrayList<Attraction> m_path = null;
	
	ArrayList<Vec2D> m_temperature_shape = new ArrayList<>();
	ArrayList<Integer> m_temperature_value = new ArrayList<>();


	Actors ChoosenAttraction = null;

	//Person(ChangCeilingColor003 _p5, Vec3D pos, Actors destiny, int type) {//basic behavior for each person-----------------------------------------------------------------------
	Person(ChangCeilingColor003 _p5, List<Attraction> path, int type) {//basic behavior for each person-----------------------------------------------------------------------
		super(_p5,path.get(0).GetLocation());
		//this.pos=pos;
		m_path = new ArrayList<Attraction>(path);
		destiny=1;//last element
		ChoosenAttraction =m_path.get(destiny) ; ///////////////////////////////////////////////
		m_type=type;
		//defining the specific characters of this agent
		Type();
		//speed
		time_to_go_to_attraction =50;
		maxVel = setVel;
		vel = new Vec3D (GetLocation().x, GetLocation().y, GetLocation().z);
		CurrentTarget = new Vec3D (GetLocation().x, GetLocation().y, GetLocation().z);
		vel.sub(m_path.get(destiny).GetLocation());
		vel.normalize();
		acc = new Vec3D(0, 0, 0);
		maxForce = 1;

		InterestDuration = 10;
		
		int mass_radios = 30;		
		for(int x = -1*mass_radios ; x <mass_radios ; ++x)
		{
			for(int y = -1*mass_radios ; y < mass_radios  ; ++y)
			{
				if((x == 0) && (y == 0))
					continue;
				double  distance_from_radio =Math.sqrt( Math.pow(x, 2 ) + Math.pow(y,2) ); 
				if( distance_from_radio <mass_radios )
				{
					m_temperature_shape.add(new Vec2D(x,y));
					
					m_temperature_value.add((int)(((mass_radios-distance_from_radio)/mass_radios)*120));
				}
			}
		}

	}
	private float random1(int i, int j) {
		// TODO Auto-generated method stub
		return 0;
	}
	public void update(World w) {///////////////////////////////////////////////////////////////////////////

		distraction(w);

		//checking the distance to the destiny -------------------------------------------------------
		float hDist = GetLocation().distanceTo(m_path.get(destiny).GetLocation());
		
		//
		if (hDist < 10) 
		{
			if(m_path.get(m_path.size()-1).GetId() == m_path.get(destiny).GetId()  )
			{
				there = true;
			}
			else
			{
				m_path.get(destiny).Transform(this);				
				++destiny; 
			}
		}
		
	}

	//going to a distraction point in a certain distance-------------------------------------------------------------------------------------------------------------------------------
	void distraction(World w ) 
	{
		if (time_pass_since_last_attraction>time_to_go_to_attraction )
		{          
			double MaxGrade =  0.0;
			if(p5.AllActiveActors.size() >0)
			{      
				Actors BestActorSoFar = p5.AllActiveActors.get(0);
				Actors OtherActorToCheck = p5.AllActiveActors.get(0);
				for (int i=0; i<p5.AllActiveActors.size(); i++) 
				{
					OtherActorToCheck = p5.AllActiveActors.get(i); 
					if(OtherActorToCheck == this)
					{
						continue;
					}

					double distance = GetLocation().distanceTo(OtherActorToCheck.GetLocation());

					if(distance<( attention_radios +OtherActorToCheck.GetAttractionRadious()))
					{
						double grade= GetAtrctivnessGrade(OtherActorToCheck);
						if( grade > MaxGrade)
						{
							MaxGrade = grade;
							BestActorSoFar = OtherActorToCheck;
						}
					}
				}      
				//BestActorSoFar.location.distanceTo(location) < attention_radios && 
				if (MaxGrade > 0 ) 
				{//attention_radios is the dis ------------------------------------------------------------------------------------------------------------------------
					distracted = true;          
					ChoosenAttraction =BestActorSoFar;
				}
			}
		}
		if( distracted==true && ChoosenAttraction != null) 
		{//after passing some time through the main destination/change the destination--------------------------------------------------------------------------
			CurrentTarget = ChoosenAttraction.GetLocation();
			goToDes(ChoosenAttraction.GetLocation(), intention);
			timer++;
			if (timer>ChoosenAttraction.GetIntrestDuration())
			{//the distraction time-------------------------------------------------------------
				time_pass_since_last_attraction = 0;
				timer = 0;
				distracted = false;
				goToDes(CurrentTarget, 2.0-intention);
			}
		}
		else 
		{
			time_pass_since_last_attraction++;//----------------------------------------------------------------------------
			ChoosenAttraction = m_path.get(destiny);
			goToDes(ChoosenAttraction.GetLocation(), intention);
		}
	}

	//v = v +||steer||*intention
	//
	void goToDes(Vec3D des, double inten) {//moving towards destination--------------------------------------------------------------------------------------------------------
		//	    println("des:(" + des.x+","+ des.y+","+ des.z+"),inten: "+inten);
		
		if(des.distanceTo(GetLocation()) > attention_radios)
			ChoosenAttraction = m_path.get(destiny);
			
		Vec3D ste = steer(new Vec3D(des.x, des.y, des.z));
		
		ste.scaleSelf((float)inten);
		vel.addSelf(ste);
		vel.limit(maxVel);
		
		Vec3D new_location = GetLocation().addSelf(vel);
		  
		SetLocation(GetLocation().addSelf(vel));
		
		int DetectionRadios = 5;
		if((new_location.x >DetectionRadios) && (new_location.x < (p5.width -DetectionRadios) )  
				&&
			(new_location.y < (p5.height -DetectionRadios) ) && (new_location.y > DetectionRadios) )
		{
			//Try to descent
			float min_temp = Float.MAX_VALUE;
			float max_temp = Float.MIN_VALUE;
			Vec3D gradientDirection = new Vec3D();
			gradientDirection.z =0;
			
			
			int GlobalCunter =0 ;
			for(int x=-DetectionRadios; x <(DetectionRadios + 1);++x)
			{
				for(int y=-DetectionRadios; y <(DetectionRadios + 1);++y)
				{
					
					double  distance_from_radio =Math.sqrt( Math.pow(x, 2 ) + Math.pow(y,2) ); 
					if((int) distance_from_radio !=  DetectionRadios)
					{
						continue;
					}			

					
					
					//p5.println(" (int)(new_location.x) + x "+((int)(new_location.x) + x ) +" (int) (new_location.y) + y " + ((int) (new_location.y) + y) );
					float current_temp =  p5.PrevTemperatureGrid[(int)(new_location.x) + x][(int) (new_location.y) + y][m_nCurrentFloor];
					if(GlobalCunter == 0)
					{
						min_temp =current_temp;
						max_temp = current_temp;
					}
					
					if(current_temp < min_temp )
					{
						gradientDirection.x = x; 	
						gradientDirection.y = y;
						min_temp  =current_temp; 
					}
					if(current_temp >max_temp )
					{
						max_temp = current_temp;
					}
					
					GlobalCunter++; 
				}
			}
			
			//if the person is on a plateau dont change its direction
			if(max_temp != min_temp)
			{
				gradientDirection.scaleSelf(min_temp);
				gradientDirection.limit(maxVel);		
				SetLocation(GetLocation().addSelf(gradientDirection));
			}
			
		}
			
		for(int i =0; i< m_temperature_shape.size();++i)
		{
			Vec2D TempLocation = GetLocation().to2DXY().add(m_temperature_shape.get(i));
			if((TempLocation.x >1) && (TempLocation.x < p5.width)  
					&&
				(TempLocation.y < p5.height) && (TempLocation.y > 1) )
			{
			 
//			p5.println("(int)TempLocation.x " +(int)TempLocation.x +" (int)TempLocation.y "+ (int)TempLocation.y +"m_temperature_value.get(i)" + m_temperature_value.get(i));

				if(m_nCurrentFloor > 0)
				{
					int x =0;
				}
			p5.TemperatureGrid[(int)TempLocation.x][(int)TempLocation.y][m_nCurrentFloor] += m_temperature_value.get(i); 	
			
			}
	
		}
		
		acc = new Vec3D(0, 0, 0);
	}
	//Steering
	Vec3D steer(Vec3D target) {
		Vec3D steer;  // The steering vector
		Vec3D desired = target.sub(GetLocation());
		float d = desired.magnitude();

		if (d > 0) {
			desired.normalize();
			desired.scaleSelf(maxVel);
			steer = desired.sub(vel);
			steer.limit(maxForce);
		} 
		else {
			steer = new Vec3D(0, 0, 0);
		}
		return steer;
	}

	Vec3D seek(Vec3D target) {
		Vec3D steer;  // The steering vector
		Vec3D desired = target.sub(GetLocation());

		desired.normalize();
		desired.scaleSelf(-5000);
		steer = vel.add(desired);
		steer.limit(maxForce);
		return steer;
	} 

	//wander
	void wander() {  
		float wanderR = 20;         
		float wanderD = 80;         
		float change = 0.5f;
		wandertheta += p5.random(-change, change);     
		Vec3D circleloc = vel.copy();
		circleloc.normalize();            
		circleloc.scaleSelf(wanderD);          
		circleloc.addSelf(GetLocation());              
		Vec3D circleOffSet = new Vec3D(wanderR*PApplet.cos(wandertheta), wanderR*PApplet.sin(wandertheta), 0);
		circleOffSet.addSelf(circleloc);
		acc.addSelf(steer(circleOffSet));
	}


	Vec3D futureLoc(float dis) {
		Vec3D diff = GetLocation().copy();
		Vec3D circleloc = vel.copy();
		circleloc.normalize();
		circleloc.scaleSelf(dis);
		diff.addSelf(circleloc);
		return diff;
	}

	//---------------------------------------------------------------
	void drawPerson() {//shape of person------------------------------------------------------------------------------------------------



	}
	////Defining different characteristics for different types of agents/define once should put in the setup ----------------------------------------------------------------------------------------------------------------------------
	void Type() {
		switch (m_type) {
		case 1 ://visitor
			//myColor = color(150, 4, 17);
			myColor = color(0, 255, 0);
			setVel = p5.random(0.5f, 0.1f);
			intention = 1.5f;
			attention_radios = 20;
			attention = (int)p5.random(60, 1200);

			DesireList.put("ArtScent", (double)p5.Attract_to_art_1);
			DesireList.put("Type1Scent", (double)p5.Attract_to_Social_1);
			DesireList.put("Type2Scent", (double)p5.Attract_to_Collector_1);
			DesireList.put("GoTheInitialDestinationScent", (double)p5.Go_to_Destination_1);


			SelfAttrationList.put("Type1Scent", (double)p5.SocialSent_1);
			SelfAttrationList.put("Type2Scent", (double)p5.CollectorSent_1);

			break;

		case 2 :
			myColor = color(255, 255, 0);
			setVel = p5.random(0.3f, 0.2f);
			intention =0.1f;
			attention_radios =40;
			attention = (int)p5.random(500, 1000);

			DesireList.put("ArtScent", (double)p5.Attract_to_art_2);//He is an artist in exhibtion, He currently not interted in other people art
			DesireList.put("Type1Scent", (double)p5.Attract_to_Social_2);//Pay some little attention to random visitor
			DesireList.put("Type2Scent", (double)p5.Attract_to_Collector_2);//Pay most of his attention to art collector
			DesireList.put("GoTheInitialDestinationScent", (double)p5.Go_to_Destination_2);

			SelfAttrationList.put("Type1Scent", (double)p5.SocialSent_2);
			SelfAttrationList.put("Type2Scent", (double)p5.CollectorSent_2);
			break;

		case 3 :
			myColor = color(50, 0, 255);
			setVel = p5.random(1, 0.2f);
			intention =0.5f;
			attention_radios =30;
			attention = (int)p5.random(500, 1000);

			DesireList.put("ArtScent", (double)p5.Attract_to_art_3);//He is an artist in exhibtion, He currently not interted in other people art
			DesireList.put("Type1Scent", (double)p5.Attract_to_Social_3);//Pay some little attention to random visitor
			DesireList.put("Type2Scent", (double)p5.Attract_to_Collector_3);//Pay most of his attention to art collector
			DesireList.put("GoTheInitialDestinationScent", (double)p5.Go_to_Destination_3);

			SelfAttrationList.put("Type1Scent", (double)p5.SocialSent_3);
			SelfAttrationList.put("Type2Scent", (double)p5.CollectorSent_3);
			break;


		}
	}
	private int color(int i, int j, int k) {
		return p5.color(i,j,k);
	}
	@Override
	public void Draw() {
		p5.stroke(myColor);//color control the pype------------------------------------------------------------------------------------------------------------------------
		if(p5.showDetail)
		{
			//p5.stroke(myColor);
			//p5.noFill();
			//p5.stroke(0,150, 150,90);
			//p5.ellipse(GetLocation().x, GetLocation().y, attention_radios, attention_radios);
			//p5.ellipse(GetLocation().x, GetLocation().y, 20, 20);
			//p5.fill(255,255,255);
			//p5.text(time_pass_since_last_attraction+"\n"+timer, GetLocation().x+5, GetLocation().y);
			//p5.line(GetLocation().x, GetLocation().y, GetLocation().z, CurrentTarget.x, CurrentTarget.y,CurrentTarget.z);
		
			//p5.line(GetLocation().x, GetLocation().y, GetLocation().z,ChoosenAttraction.GetLocation().x,ChoosenAttraction.GetLocation().y,ChoosenAttraction.GetLocation().z);      
		}

		
		//agents head 
		//p5.strokeWeight(8);////////////////////////////////////////////////////
		p5.noStroke();
		p5.translate(GetLocation().x, GetLocation().y, GetLocation().z+10);
		//p5.point(GetLocation().x, GetLocation().y, GetLocation().z+pHt);
		p5.fill(myColor);
		//p5.sphere((float) 4); //////SiteA
		p5.sphere((float) 1); //////SiteA
		//p5.sphere((float) 8); /////SiteB
		p5.translate(-GetLocation().x, -GetLocation().y, -(GetLocation().z+10));
		
		//agents body
		p5.stroke(myColor);
		p5.strokeWeight(1);
		p5.line(GetLocation().x,GetLocation().y,GetLocation().z, GetLocation().x,GetLocation().y,GetLocation().z+10);

	}
	@Override
	double GetIntrestDuration() {
		return InterestDuration;
	}
	@Override
	public double GetAttractionRadious() {
		// TODO Auto-generated method stub
		return AttractionRadious;
	}

}
